use crate::db::entities::tag_relation::{ActiveModel, CREATE_QUERY, Column, Entity, Model, TABLE_COLUMNS, TABLE_NAME};
use rbase::define::ApiError;
use rbase::define::BaseError;
use rbase::id_utils::gen_id;
use rbase::time_utils::get_systemtime_millis;

use anyhow::Result;
use anyhow::anyhow;
use log::info;
use rbase::db::{self, get_connection};
use sea_orm::ActiveValue::Set;
use sea_orm::prelude::Expr;
use sea_orm::{ColumnTrait, EntityTrait, QueryFilter};

pub async fn init_tables() -> Result<()> {
    info!("Initializing tables");
    db::init_tables(TABLE_NAME, CREATE_QUERY, &TABLE_COLUMNS).await?;
    Ok(())
}

pub async fn insert(model: Model) -> Result<Model> {
    let connection = get_connection()?;

    let mut active_model = ActiveModel::from(model);

    let id = gen_id()?;
    active_model.id = Set(id);

    let systemtime = get_systemtime_millis();
    active_model.created_at = Set(Some(systemtime));
    active_model.modified_at = Set(Some(systemtime));

    Entity::insert(active_model).exec(&connection).await?;
    let model = find_by_id(id).await?.ok_or_else(|| anyhow!("Failed to insert tag_relation"))?;
    Ok(model)
}

pub async fn update(model: Model) -> Result<()> {
    let connection = get_connection()?;

    let mut update = Entity::update_many();
    if let Some(tag_id) = model.tag_id {
        update = update.col_expr(Column::OtherId, Expr::value(tag_id));
    };
    if let Some(other_id) = model.other_id {
        update = update.col_expr(Column::OtherId, Expr::value(other_id));
    };
    if let Some(created_at) = model.created_at {
        update = update.col_expr(Column::CreatedAt, Expr::value(created_at));
    };
    update = update.col_expr(Column::ModifiedAt, Expr::value(get_systemtime_millis()));
    let update_result = update.filter(Column::Id.eq(model.id)).exec(&connection).await?;

    if update_result.rows_affected == 1 {
        Ok(())
    } else {
        Err(anyhow!("Failed to update tag_relation"))
    }
}

pub async fn delete_by_id(id: i64) -> Result<()> {
    let connection = get_connection()?;

    Entity::delete(ActiveModel {
        id: Set(id),
        ..Default::default()
    })
    .exec(&connection)
    .await
    .map_err(|e| anyhow!("Error deleting tag_relation: {:?}", e))?;

    Ok(())
}

pub async fn delete_by_tag_id(tag_id: i64) -> Result<()> {
    let connection = get_connection()?;

    Entity::delete_many()
        .filter(Column::TagId.eq(tag_id))
        .exec(&connection)
        .await
        .map_err(|e| anyhow!("Error deleting tag_relation: {:?}", e))?;

    Ok(())
}

pub async fn delete_by_other_id(other_id: i64) -> Result<()> {
    let connection = get_connection()?;

    Entity::delete_many()
        .filter(Column::OtherId.eq(other_id))
        .exec(&connection)
        .await
        .map_err(|e| anyhow!("Error deleting tag_relation: {:?}", e))?;

    Ok(())
}

pub async fn find_by_id(id: i64) -> Result<Option<Model>> {
    let connection = get_connection()?;

    Entity::find()
        .filter(Column::Id.eq(id))
        .one(&connection)
        .await
        .map_err(|e| anyhow!("Error finding tag_relation: {:?}", e))
}

pub async fn find_by_tag_id_and_other_id(tag_id: i64, other_id: i64) -> Result<Option<Model>> {
    let connection = get_connection()?;

    Entity::find()
        .filter(Column::TagId.eq(tag_id).and(Column::OtherId.eq(other_id)))
        .one(&connection)
        .await
        .map_err(|e| anyhow!("Error finding tag_relation: {:?}", e))
}

pub async fn find_by_tag_id(tag_id: i64) -> Result<Vec<Model>> {
    let connection = get_connection()?;

    let result = Entity::find().filter(Column::TagId.eq(tag_id)).all(&connection).await?;
    Ok(result)
}

pub async fn find_by_other_id(other_id: i64) -> Result<Vec<Model>, ApiError> {
    let connection = get_connection()?;

    let result = Entity::find()
        .filter(Column::OtherId.eq(other_id))
        .all(&connection)
        .await
        .map_err(|e| BaseError::OperationFailed(e.to_string()))?;
    Ok(result)
}

pub async fn find_by_other_id_and_owner_ids(other_id: i64, owner_ids: &Option<Vec<i64>>) -> Result<Vec<Model>, ApiError> {
    let connection = get_connection()?;

    let result = Entity::find()
        .filter(match owner_ids {
            Some(owner_ids) => Column::OtherId.eq(other_id).add(Column::OwnerId.is_in(owner_ids.clone())),
            None => Column::OtherId.eq(other_id),
        })
        .all(&connection)
        .await
        .map_err(|e| BaseError::OperationFailed(e.to_string()))?;
    Ok(result)
}
